package com.lagou.java;

import com.lagou.java.service.UserService;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import lombok.extern.slf4j.Slf4j;
import sun.rmi.runtime.Log;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Handler;

/**
 * @Classname RpcConsumer
 * @Description TODO
 * @Date 2020/7/22 13:11
 * @Created by pc
 */
@Slf4j
public class RpcConsumer {

    private static ExecutorService executor =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private static final String CLIENT_NAME_PREFIX = "NETTY-CLIENT-";

    public static synchronized String getClientName() {
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CLIENT_NAME_PREFIX + System.currentTimeMillis();
    }

    /**
     * 创建一个代理对象
     */
    public  static UserService createProxy(final Class<?> serviceClass, String host, int port, String clientName) {
        ClientBootstrap.AddressInfo addressInfo = new ClientBootstrap.AddressInfo(clientName, host + ":" + port);
        UserService service = (UserService)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{serviceClass}, (proxy, method, args) -> {
                    // 初始化 netty 客户端
                    ClientBootstrap.ClientInfo client = ClientBootstrap.addressesWithHandlers.get(addressInfo);
                    if (client == null || client.userClientHandler == null) {
                        client = initClient(host, port);
                        ClientBootstrap.addressesWithHandlers.put(addressInfo, client);
                    }
                    UserClientHandler handler = client.userClientHandler;
                    // 设置参数
//                    client.setPara(providerName + args[0]);
                    RpcRequest request = new RpcRequest();
                    request.setRequestId(UUID.randomUUID().toString());
                    request.setClassName(serviceClass.getName());
                    request.setMethodName(method.getName());
                    request.setParameterTypes(method.getParameterTypes());
                    request.setParameters(args);

                    handler.setData(request);

                    // 作业二：打乱时间， 模拟业务代码 的 执行时间波动/网络通讯波动
                    int restTime = new Random().nextInt(20);
//                    TimeUnit.SECONDS.sleep(restTime);
//                    log.info("===>>> 服务" + host + ":" + port + "休息了 " + restTime + "秒");
                    return executor.submit(handler).get();

                });
        ClientBootstrap.ClientInfo client = ClientBootstrap.addressesWithHandlers.get(addressInfo);
        if (client == null) {
            client = RpcConsumer.initClient(host, port);
            ClientBootstrap.addressesWithHandlers.put(addressInfo, client);
        }
        client.userService = service;
        return service;
    }

    public static ClientBootstrap.ClientInfo initClient(String host, int port) {
        ClientBootstrap.ClientInfo clientInfo = new ClientBootstrap.ClientInfo();

        final UserClientHandler client = new UserClientHandler();
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();

        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
//                        pipeline.addLast(new StringEncoder());
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new RpcEncoder(RpcRequest.class, new JSONSerializer()));
                        pipeline.addLast(client);
                    }
                });
        try {
            ChannelFuture future = bootstrap.connect(host, port).sync();

            clientInfo.userClientHandler = client;
            clientInfo.bootstrap = bootstrap;
            clientInfo.group = group;
            clientInfo.channelFuture = future;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return clientInfo;
    }
}
